# loader.asm
#
# Entry point into kernel and defines multiboot header
#
# Copyright 2008 Matthew Iselin

# define all the flags to use in the multiboot header
.set MULTIBOOT_PAGE_ALIGN,		1 << 0		# align the kernel and its modules on a page boundary (ie, 4k)
.set MULTIBOOT_MEMORY_INFO,		1 << 1		# provide the kernel with a memory map, amongst other memory information
.set MULTIBOOT_HEADER_MAGIC,	0x1BADB002	# multiboot magic value (defined in the standard)
.set MULTIBOOT_HEADER_FLAGS,	(MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO) # flags for the header
.set MULTIBOOT_CHECKSUM,		-(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) # the negative of the sum

# the header should ALWAYS go first
.align 4
.long MULTIBOOT_HEADER_MAGIC
.long MULTIBOOT_HEADER_FLAGS
.long MULTIBOOT_CHECKSUM

# reserve space in the BSS section for the stack (32k)
.set STACK_SIZE, 0x10000
.comm stack, STACK_SIZE, 32		# keep it on a quadword boundary, and create the "stack" symbol for use

# the entry point
.global _loader
_loader:

	# setup the stack
	mov $(stack + STACK_SIZE),%esp

	# push the multiboot information structure
	push %ebx

	# call the kernel
	call kmain

	# we've returned, so clear the interrupt flag, and halt the CPU
	cli
	hlt

# performs a context switch
.global ctxswitch
ctxswitch:
    pusha						# push all general purpose registers
    lea 36(%esp),%eax			# grab the effective addres of the pointer passed
	mov (%eax),%eax				# grab the address of the sturcture it points to
    movl %esp,(%eax)			# save the current ESP into that address
    # movl 44(%esp),%ebp			# load EBP
    movl 44(%esp),%eax			# load CR3 (two-step process)
    movl %eax,%cr3				# actually dump cr3 into the register
    movl 40(%esp),%esp          # load the new ESP
    popa						# pop all general purpose registers
    ret

# reads the EIP value
.global readeip
readeip:
	pop %eax					# pop the return location into EAX (EAX = return value location)
	jmp *%eax

# flushes segment registers and loads the new GDT
.global gdt_flush
gdt_flush:
	mov 4(%esp),%eax
	lgdt (%eax)
	jmp $0x08,$.flush
.flush:
	mov $0x10,%ax
	mov %ax,%ds
	mov %ax,%es
	mov %ax,%fs
	mov %ax,%gs
	mov %ax,%ss
	ret

# loads the new IDT
.global idt_flush
idt_flush:
	mov 4(%esp),%eax
	lidt (%eax)
	ret

# macros for ISR and IRQ handlers
.macro ISRHANDLER IntNum
	.global isr_\IntNum
	isr_\IntNum:
		cli
		pushl $0
		pushl $\IntNum
		jmp IsrCommonStub
.endm
.macro ISRHANDLER_ECODE IntNum
	.global isr_\IntNum
	isr_\IntNum:
		cli
		pushl $\IntNum
		jmp IsrCommonStub
.endm
.macro IRQHANDLER IrqNum IntNum
	.global irq_\IrqNum
	irq_\IrqNum:
		cli
		pushl $0
		pushl $\IntNum
		jmp IrqCommonStub
.endm
.macro INTERRUPT_COMMON_STUB ProcName Calls
	\ProcName:
		pusha								# push all the general purpose registers

		push %ds							# push segment registers
		push %es
		push %fs
		push %gs

		mov $0x10,%ax						# load the kernel segments
		mov %ax,%ds
		mov %ax,%es
		mov %ax,%fs
		mov %ax,%gs

		mov %esp,%eax						# push the address of the stack so that the handler can get information about it
		push %esp
		call \Calls							# call the handler
		pop %eax

		pop %gs								# pop the segment registers
		pop %fs
		pop %es
		pop %ds

		popa								# pop the general purpose register

		add $8, %esp						# skip the interrupt number and other junk on the stack

		sti									# enable interrupts

		iret								# return from the interrupt
.endm

# setup all of the ISRs (32 of them)
ISRHANDLER 0
ISRHANDLER 1
ISRHANDLER 2
ISRHANDLER 3
ISRHANDLER 4
ISRHANDLER 5
ISRHANDLER 6
ISRHANDLER 7
ISRHANDLER_ECODE 8
ISRHANDLER 9
ISRHANDLER_ECODE 10
ISRHANDLER_ECODE 11
ISRHANDLER_ECODE 12
ISRHANDLER_ECODE 13
ISRHANDLER_ECODE 14
ISRHANDLER 15
ISRHANDLER 16
ISRHANDLER 17
ISRHANDLER 18
ISRHANDLER 19
ISRHANDLER 20
ISRHANDLER 21
ISRHANDLER 22
ISRHANDLER 23
ISRHANDLER 24
ISRHANDLER 25
ISRHANDLER 26
ISRHANDLER 27
ISRHANDLER 28
ISRHANDLER 29
ISRHANDLER 30
ISRHANDLER 31
ISRHANDLER 128

# setup all of the IRQs (16 of them)
IRQHANDLER 0 32
IRQHANDLER 1 33
IRQHANDLER 2 34
IRQHANDLER 3 35
IRQHANDLER 4 36
IRQHANDLER 5 37
IRQHANDLER 6 38
IRQHANDLER 7 39
IRQHANDLER 8 40
IRQHANDLER 9 41
IRQHANDLER 10 42
IRQHANDLER 11 43
IRQHANDLER 12 44
IRQHANDLER 13 45
IRQHANDLER 14 46
IRQHANDLER 15 47

# the common stubs for all ISRs and IRQs
INTERRUPT_COMMON_STUB IsrCommonStub kIsrCommonHandler
INTERRUPT_COMMON_STUB IrqCommonStub kIrqCommonHandler

.global v8086test
.code16
v8086test:
#    mov $0x13,%ax
#    int $0x10
	# mov $0xdead,%ax
	int $0x80
.code32
